#include "usart.h"

char USART1_RX_STR[USART1_REC_LEN] = {0}; // 接收String
uint8_t USART1_RX_FLAG = 0;
uint8_t USART1_TX_BUF[USART1_SEND_LEN];

void USART1_Init(void)
{

    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    //
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 时钟源启动
    //
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // USART1 Tx PA9配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;            // USART1 Rx PA10配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    //
    USART_InitStructure.USART_BaudRate = 115200;                                    // 波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;                     // 数据位8
    USART_InitStructure.USART_StopBits = USART_StopBits_1;                          // 停止位1
    USART_InitStructure.USART_Parity = USART_Parity_No;                             // 校验位无
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 流控制无
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;                 // 串口模式
    USART_Init(USART1, &USART_InitStructure);                                       // 串口初始化
    USART_Cmd(USART1, ENABLE);                                                      // 串口使能
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);                                  // 串口接收中断使能
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);                                 // 中断优先级组配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;                               // 中断请求通道选择
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;                              // 抢占优先级
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 10;                      // 相应优先级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                                 // 中断请求通道使能
    NVIC_Init(&NVIC_InitStructure);                                                 // 中断初始化
    USART1_DMA_Init();                                                              //
}

void USART1_DMA_Init(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    memset(USART1_TX_BUF, 00, USART1_SEND_LEN);
    // DMA1-CH4 (存贮器->外设)
    DMA_DeInit(DMA1_Channel4);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(USART1->DR));   // 外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)(USART1_TX_BUF);       // 存贮器地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 外设为目标
    DMA_InitStructure.DMA_BufferSize = USART1_SEND_LEN;                     // 数据个数
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址是否递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 存贮器地址是否递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 存贮器数据宽度
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;                     // 优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // DMA_memory_to_memory
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);
}

void USART1_DMA_Send(void)
{
    // 判断是否发送完成: (DMA_GetFlagStatus(DMA1_FLAG_TC4) == SET);
    // DMA_ClearFlag(DMA1_FLAG_TC4);
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    DMA_Cmd(DMA1_Channel4, DISABLE);
    DMA_SetCurrDataCounter(DMA1_Channel4, USART1_SEND_LEN);
    DMA_Cmd(DMA1_Channel4, ENABLE);
}

/**
 * @note [USART1发送一个字节]
 */
void USART1_SendByte(uint8_t byte)
{
    USART_SendData(USART1, byte);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        ; // 等待发送完成
}

/**
 * @brief USART1单独的printf
 * @note 封装[vsnprintf]
 */
void USART1_printf(char *fmt, ...)
{
    char buffer[200];
    uint8_t i = 0;
    va_list arg_ptr;
    va_start(arg_ptr, fmt);
    vsnprintf(buffer, 201, fmt, arg_ptr); // 格式化打印内容到buffer
    va_end(arg_ptr);
    while ((i < 200) && (i < strlen(buffer))) // 发送buffer
    {
        USART_SendData(USART1, (uint8_t)buffer[i++]);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
            ; // 等待发送完成
    }
}
/**
 * @note (USART1_RX_FLAG==1):(接收String完成)
 * @note (USART1_RX_FLAG==0):(当前能接收String)
 */
uint8_t USART1_GetRxFlag(void)
{
    return USART1_RX_FLAG;
}

/**
 * @note 清除USART1_RX_FLAG,以便下一次接收String
 */
void USART1_ClearRxFlag(void)
{
    memset(USART1_RX_STR, 0, USART1_REC_LEN); // 清除
    USART1_RX_FLAG = 0;
}

/**
 * @brief 通过查询方式接收字节
 * @attention 查询方式要关掉[USART1接收中断]
 */
uint8_t USART1_ReceiveByte(void)
{
    uint8_t byte = 0;
    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET) // 查询[收到数据]
    {
        byte = USART_ReceiveData(USART1); // 读(USART1->DR)后,RXNE清零
        USART1_SendByte(byte);            // 回显
    }
    return byte;
}

/**
 * @brief USART1中断服务程序,用于接收String
 * @attention String格式: (@xxxxxx\r\n);以@开头,\r\n结束
 */
void USART1_IRQHandler(void)
{
    uint8_t data = 0;
    static uint8_t RxState = 0;                          //
    static uint8_t RxIndex = 0;                          //
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) // 是接收中断
    {
        data = USART_ReceiveData(USART1); // 读(USART1->DR)后,RXNE清零
        USART1_SendByte(data);            // 回显
        if ((USART1_RX_FLAG == 0))        // 可接收状态
        {
            if ((RxState == 0)) // 开头
            {
                if (data == '@') // 以@开头
                {
                    RxState = 1;
                    RxIndex = 0;
                }
            }
            else if (RxState == 1) //
            {
                if (data == '\r') // 接收到\r(回车)=0x0d
                {
                    RxState = 2;
                }
                else
                {
                    // 接收字符
                    USART1_RX_STR[RxIndex] = data;
                    RxIndex++;
                    if (RxIndex > (USART1_REC_LEN - 2)) // 留最后一个位置给'\0'
                    {
                        RxState = 0; // 重新接收
                        memset(USART1_RX_STR, 0, USART1_REC_LEN);
                    }
                }
            }
            else if (RxState == 2) // [最后一个字节]
            {
                if (data == '\n') //  \n(换行)=0x0a;接收完成
                {
                    USART1_RX_FLAG = 1;            // 接收完成
                    USART1_RX_STR[RxIndex] = '\0'; // 字符串结束
                    RxState = 0;                   // 可进入[下一次接收]
                }
                else
                {
                    // 最后一个字节不是\n;
                    RxState = 0; // 重新接收
                    memset(USART1_RX_STR, 0, USART1_REC_LEN);
                }
            }
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE); //
    }
}

#if 1
/* 告知连接器不从C库链接使用半主机的函数 */
#pragma import(__use_no_semihosting)

/**
 * @note 定义 _sys_exit() 以避免使用半主机模式
 */
void _sys_exit(int x)
{
    x = x;
}

struct __FILE
{
    int handle;
}; // 标准库需要的支持类型

FILE __stdout;
FILE __stdin;

/**
 * @brief 重定向printf到USART1发送
 * @attention printf只能用在一个USARTx上
 * @example printf("STM32 USART Printf \r\n")
 */
int fputc(int ch, FILE *stream)
{
    USART1_SendByte(ch);
    return ch;
}

/**
 * @brief 重定向scanf到USART1接收
 * @attention [接收中断]与scanf不能同时工作;先关闭接收中断,然后等待接收,最后再开启中断
 * @example scanf("%d", &num); scanf("%s", str);
 */
int fgetc(FILE *f)
{
    int data = 0;
    USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET)
        ; // 等待串口1输入数据
    data = (int)USART1_ReceiveByte();
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    return data;
}

#endif
